
const CryptoJS = require('./crypto-js-sha1.js')

// 需要CryptoJS
var HOTP = function(secret, window, counter) {
    this.secret = secret || "123456";
    this.window = window || 3;
    this.counter = counter || 0;
};

HOTP.prototype.gen = function(counter) {
    if (counter === null) {
        if (this.counter === null) {
            this.counter = 0;
        }
        counter = this.counter;
        var ret = this.gen(counter);
        this.counter += 1;
        return ret;
    }

    var num = this.otp_gen_number(counter, this.secret);
    num = num % 1000000;
    if (num >= 100000) {
        return String(num);
    }
    num = "000000" + num;
    return num.substring(num.length - 6);
}

HOTP.prototype.verify = function(token, counter) {
    if (counter === undefined) {
        if (this.counter === undefined) {
            this.counter = 0;
        }
        counter = this.counter;
        return this.verify(token, counter);
    }
    for (var i = 0; i < this.window; i++) {
        {
            var tcounter = counter - i;
            if (token === this.gen(tcounter))
                return i;
        }
        if (i === 0)
            continue;
        {
            var tcounter = counter + i;
            if (token === this.gen(tcounter))
                return i;
        }
    }
    return null;
}

// 需要使用CryptoJS的HmacSHA1、WordArray，counter是32bit数字
HOTP.prototype.otp_gen_number = function(counter, secret) {
    function convert(wordArray) {
        var bytes20 = [];
        for (var i = 0, j = 0; i < 5; i++) {
            var v = wordArray.words[i];
            bytes20[j++] = ((v >> 24) & 0xFF);
            bytes20[j++] = ((v >> 16) & 0xFF);
            bytes20[j++] = ((v >>  8) & 0xFF);
            bytes20[j++] = ((v >>  0) & 0xFF);
        }

        var offset = bytes20[19] & 0xf;
        var v = (bytes20[offset] & 0x7F) << 24 |
            (bytes20[offset + 1] & 0xFF) << 16 |
            (bytes20[offset + 2] & 0xFF) << 8  |
            (bytes20[offset + 3] & 0xFF);

        return v;
    }

    function _gen_number(counter, secret) {
        var low = counter & 0xffffffff;
        var high = Math.floor(counter / 4294967296); // 没有long类型，使用除以2^32代替右移32位
        // console.log(counter, low, high);
        var message = CryptoJS.lib.WordArray.create([high, low], 8);
        var wordArray = CryptoJS.HmacSHA1(message, secret);
        var num = convert(wordArray);
        return num;
    }

    return _gen_number(counter, secret);
}

// ==================================================================================
var TOTP = function(secret, window, interval) {
    this.hotp = new HOTP(secret, window, 0);
    this.interval = interval || 30;
}

TOTP.prototype.gen = function(timestamp) {
    timestamp = timestamp || new Date().getTime();
    var timestamp_second = Math.floor(timestamp / 1000);
    var tcounter = Math.floor(timestamp_second / this.interval);
    return this.hotp.gen(tcounter);
}

TOTP.prototype.gen2 = function(timestamp) {
    timestamp = timestamp || new Date().getTime();
    var timestamp_second = Math.floor(timestamp / 1000);
    var tcounter = Math.floor(timestamp_second / this.interval);
    var token = this.hotp.gen(tcounter);
    var step = timestamp_second % this.interval;
    var refresh_time = tcounter * this.interval;
    return {
        token: token, // 生成的令牌
        loop_length: this.interval, // 循环角度，循环长度
        loop_step: step, // 循环角度，循环到的步骤
        refresh_interval: this.interval * 1000, // 刷新周期
        refresh_time: refresh_time * 1000, // 刷新时间
        timestamp: timestamp, // 当前时间
    };
}

TOTP.prototype.verify = function(token) {
    var now = Math.floor(new Date().getTime() / 1000);
    var tcounter = Math.floor(now / this.interval);
    return this.hotp.verify(token, tcounter);
}

module.exports = {
    HOTP: HOTP,
    TOTP: TOTP,
}